package com.doubao.wechat.service.impl;

import com.doubao.common.result.Result;
import com.doubao.common.utils.RedisTemplateFactory;
import com.doubao.user.dto.response.UserInfoResponse;
import com.doubao.user.feign.UserFeignClient;
import com.doubao.wechat.dto.request.WxLoginRequest;
import com.doubao.wechat.dto.response.WxUserInfoDTO;
import com.doubao.wechat.service.WechatService;
import com.doubao.wechat.util.WxBizDataCrypt;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 微信服务实现类
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class WechatServiceImpl implements WechatService {

    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;
    private final UserFeignClient userFeignClient; // 注入 UserFeignClient
    private RedisTemplate<String, Object> redisTemplate;

    @PostConstruct
    public void init() {
        this.redisTemplate = RedisTemplateFactory.getRedisTemplate();
        log.info("WechatServiceImpl使用RedisTemplateFactory获取RedisTemplate");
    }

    @Value("${wechat.miniapp.appid}")
    private String miniappAppid;

    @Value("${wechat.miniapp.secret}")
    private String miniappSecret;

    private static final String WX_SESSION_KEY_URL = "https://api.weixin.qq.com/sns/jscode2session";
    private static final String WX_SESSION_CACHE_PREFIX = "wx:session:";
    private static final long WX_SESSION_CACHE_EXPIRE = 7200;

    @Override
    public Result<WxUserInfoDTO> miniappLogin(WxLoginRequest request) {
        try {
            // 1. 获取session_key和openid
            Map<String, Object> sessionMap = getSessionKey(request.getCode());
            if (sessionMap.containsKey("errcode")) {
                log.error("获取微信session_key失败: {}", sessionMap);
                return Result.failed("获取微信session_key失败: " + sessionMap.get("errmsg"));
            }

            String sessionKey = (String) sessionMap.get("session_key");
            String openid = (String) sessionMap.get("openid");
            String unionid = (String) sessionMap.getOrDefault("unionid", "");

            // 2. 解密用户信息
            Map<String, Object> userDataMap = decryptData(sessionKey, request.getEncryptedData(), request.getIv());
            if (userDataMap == null) {
                return Result.failed("解密用户信息失败");
            }

            // 3. 构建用户信息对象
            WxUserInfoDTO userInfoDTO = new WxUserInfoDTO();
            userInfoDTO.setOpenid(openid);
            userInfoDTO.setUnionid(StringUtils.isNotBlank(unionid) ? unionid : (String) userDataMap.getOrDefault("unionId", ""));
            userInfoDTO.setSessionKey(sessionKey);
            userInfoDTO.setNickName((String) userDataMap.get("nickName"));
            userInfoDTO.setAvatarUrl((String) userDataMap.get("avatarUrl"));
            userInfoDTO.setGender((Integer) userDataMap.get("gender"));
            userInfoDTO.setCountry((String) userDataMap.get("country"));
            userInfoDTO.setProvince((String) userDataMap.get("province"));
            userInfoDTO.setCity((String) userDataMap.get("city"));
            userInfoDTO.setLanguage((String) userDataMap.get("language"));

            @SuppressWarnings("unchecked")
            Map<String, Object> watermark = (Map<String, Object>) userDataMap.get("watermark");
            userInfoDTO.setWatermark(watermark);
            userInfoDTO.setRawData(userDataMap);

            // 不再本地保存/更新，返回解密后的信息，由上层服务决定如何处理
            // （例如，认证服务在认证成功后调用 user 服务更新信息）
            // saveOrUpdateWechatUser(userInfoDTO, null); // 移除或注释掉，或者需要传入userId

            return Result.success(userInfoDTO);
        } catch (Exception e) {
            log.error("微信小程序登录失败", e);
            return Result.failed("微信小程序登录失败: " + e.getMessage());
        }
    }

    @Override
    public Map<String, Object> getSessionKey(String code) {
        // 先从缓存获取
        String cacheKey = WX_SESSION_CACHE_PREFIX + code;
        Object cachedSession = redisTemplate.opsForValue().get(cacheKey);
        if (cachedSession != null) {
            try {
                @SuppressWarnings("unchecked")
                Map<String, Object> map = (Map<String, Object>) cachedSession;
                return map;
            } catch (ClassCastException e) {
                log.warn("缓存中的 session key 类型不正确, key: {}", cacheKey);
                redisTemplate.delete(cacheKey); // 删除错误类型的缓存
            }
        }

        // 构建请求URL
        String url = String.format(
                "%s?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
                WX_SESSION_KEY_URL, miniappAppid, miniappSecret, code
        );

        try {
            // 发送请求获取session_key和openid，并获取响应体为字符串
            ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
            String responseBody = response.getBody();

            if (responseBody != null) {
                // 手动解析JSON字符串为Map
                @SuppressWarnings("unchecked")
                Map<String, Object> result = objectMapper.readValue(responseBody, Map.class);

                // 如果获取成功，缓存结果
                if (!result.containsKey("errcode")) {
                    redisTemplate.opsForValue().set(cacheKey, result, WX_SESSION_CACHE_EXPIRE, TimeUnit.SECONDS);
                }

                return result;
            } else {
                Map<String, Object> errorResult = new HashMap<>();
                errorResult.put("errcode", -1);
                errorResult.put("errmsg", "获取微信session_key响应为空");
                return errorResult;
            }
        } catch (Exception e) {
            log.error("获取微信session_key失败", e);
            Map<String, Object> errorResult = new HashMap<>();
            errorResult.put("errcode", -1);
            errorResult.put("errmsg", "获取微信session_key异常: " + e.getMessage());
            return errorResult;
        }
    }

    @Override
    public Map<String, Object> decryptData(String sessionKey, String encryptedData, String iv) {
        if (StringUtils.isAnyBlank(sessionKey, encryptedData, iv)) {
            log.error("解密参数不完整: sessionKey={}, encryptedData={}, iv={}",
                    sessionKey != null, encryptedData != null, iv != null);
            return null;
        }

        try {
            WxBizDataCrypt wxBizDataCrypt = new WxBizDataCrypt(miniappAppid, sessionKey, objectMapper);
            return wxBizDataCrypt.decrypt(encryptedData, iv);
        } catch (Exception e) {
            log.error("微信数据解密失败", e);
            return null;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> saveOrUpdateWechatUser(WxUserInfoDTO wxUserInfo, Long userId) {
        if (wxUserInfo == null || StringUtils.isBlank(wxUserInfo.getOpenid())) {
            log.error("微信用户信息不完整");
            return Result.failed("微信用户信息不完整");
        }
        if (userId == null) {
            log.error("用户ID不能为空");
            return Result.failed("用户ID不能为空");
        }

        try {
            // 使用 bindUserWechatInfo 方法来更新微信信息，因为没有 updateRequest 方法
            String openid = wxUserInfo.getOpenid();
            String unionid = wxUserInfo.getUnionid();

            log.info("调用用户服务更新微信信息, userId={}, openid={}, unionid={}", userId, openid, unionid);
            Result<Void> updateResult = userFeignClient.bindUserWechatInfo(userId, openid, unionid);

            if (updateResult.isSuccess()) {
                log.info("成功调用用户服务更新微信信息, userId={}", userId);
                return Result.success();
            } else {
                log.error("调用用户服务更新微信信息失败, userId={}, error: {}", userId, updateResult.getMessage());
                return Result.failed("调用用户服务更新微信信息失败: " + updateResult.getMessage());
            }
        } catch (Exception e) {
            log.error("更新微信用户信息时发生异常, userId={}", userId, e);
            return Result.failed("更新微信用户信息时发生异常: " + e.getMessage());
        }
    }

    @Override
    @Deprecated
    public Void getByOpenid(String openid, String openidType) {
        log.warn("getByOpenid 方法已过时，请调用用户服务查询用户信息");
        return null;
    }

    @Override
    @Deprecated
    public Void getByUnionid(String unionid) {
        log.warn("getByUnionid 方法已过时，请调用用户服务查询用户信息");
        return null;
    }

    @Override
    @Deprecated
    public Void getByUserId(Long userId) {
        log.warn("getByUserId 方法已过时，请调用用户服务查询用户信息");
        return null;
    }

    @Override
    @Deprecated
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> bindUser(Long wechatUserId, Long userId) {
        log.warn("bindUser 方法已过时，请调用用户服务进行绑定");
        return Result.failed("此方法已弃用，请调用用户服务接口");
    }

    @Override
    @Deprecated
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> unbindUser(Long userId) {
        log.warn("unbindUser 方法已过时，请调用用户服务进行解绑");
        return Result.failed("此方法已弃用，请调用用户服务接口");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> bindOpenidToUser(Long userId, String code) {
        try {
            if (userId == null || StringUtils.isBlank(code)) {
                return Result.failed("参数错误");
            }

            // 1. 获取session_key和openid
            Map<String, Object> sessionMap = getSessionKey(code);
            if (sessionMap == null || sessionMap.containsKey("errcode")) {
                log.error("获取微信session_key失败: {}", sessionMap);
                return Result.failed("获取微信登录信息失败: " + (sessionMap != null ? sessionMap.get("errmsg") : "未知错误"));
            }

            String openid = (String) sessionMap.get("openid");
            String unionid = (String) sessionMap.getOrDefault("unionid", "");

            if (StringUtils.isBlank(openid)) {
                return Result.failed("获取微信openid失败");
            }

            // 2. 调用用户服务进行绑定
            log.info("准备调用用户服务绑定openid: userId={}, openid={}, unionid={}", userId, openid, unionid);
            Result<Void> bindResult = userFeignClient.bindUserWechatInfo(userId, openid, unionid);

            if (bindResult.isSuccess()) {
                log.info("成功调用用户服务绑定用户ID: {} 与微信openid: {}", userId, openid);
                return Result.success();
            } else {
                log.error("调用用户服务绑定openid失败: userId={}, openid={}, error: {}", userId, openid, bindResult.getMessage());
                return Result.failed("绑定微信账号失败: " + bindResult.getMessage());
            }

        } catch (Exception e) {
            log.error("绑定微信openid到用户失败", e);
            return Result.failed("绑定微信账号失败: " + e.getMessage());
        }
    }
}